// PHZ
// 2018-6-7

#if defined(WIN32) || defined(_WIN32)
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif

#include "H265Source.h"
#include <cstdio>
#include <cstring>
#include <chrono>
#if defined(WIN32) || defined(_WIN32)

#else
#include <sys/time.h>
#endif

using namespace xop;
using namespace std;

H265Source::H265Source(const uint32_t framerate) : framerate_(framerate)
{
	payload_ = 96;
	media_type_ = MediaType::H265;
	clock_rate_ = 90000;
}

H265Source *H265Source::CreateNew(const uint32_t framerate)
{
	return new H265Source(framerate);
}

H265Source::~H265Source() = default;

string H265Source::GetMediaDescription(const uint16_t port)
{
	char buf[100] = {0};
	sprintf(buf, "m=video %hu RTP/AVP 96", port);

	return buf;
}

string H265Source::GetAttribute()
{
	return "a=rtpmap:96 H265/90000";
}

bool H265Source::HandleFrame(const MediaChannelId channelId,
			     const AVFrame frame)
{
	uint8_t *frame_buf = frame.buffer.get();
	size_t frame_size = frame.size;
	RtpPacket rtp_pkt;

	rtp_pkt.type = frame.type;
	//rtp_pkt.timestamp = frame.timestamp == 0 ? GetTimestamp() : frame.timestamp;
	rtp_pkt.timestamp = frame.timestamp;

	if (frame_size <= MAX_RTP_PAYLOAD_SIZE) {
		rtp_pkt.size = static_cast<uint16_t>(frame_size) +
			       RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE;
		rtp_pkt.last = 1;

		memcpy(rtp_pkt.data.get() + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE,
		       frame_buf, frame_size);

		if (send_frame_callback_) {
			if (!send_frame_callback_(channelId, rtp_pkt)) {
				return false;
			}
		}
	} else {
		const char nalUnitType = (frame_buf[0] & 0x7E) >> 1;
		char FU[3] = {static_cast<char>(frame_buf[0] & 0x81 | 49 << 1),
			      static_cast<char>(frame_buf[1]),
			      static_cast<char>(0x80 | nalUnitType)};

		frame_buf += 2;
		frame_size -= 2;

		while (frame_size + 3 > MAX_RTP_PAYLOAD_SIZE) {
			rtp_pkt.size = RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE +
				       MAX_RTP_PAYLOAD_SIZE;
			rtp_pkt.last = 0;
			uint8_t *rtp_pkt_data = rtp_pkt.data.get() +
						RTP_TCP_HEAD_SIZE +
						RTP_HEADER_SIZE;

			*(rtp_pkt_data++) = FU[0];
			*(rtp_pkt_data++) = FU[1];
			*(rtp_pkt_data++) = FU[2];
			memcpy(rtp_pkt_data, frame_buf,
			       MAX_RTP_PAYLOAD_SIZE - 3);

			if (send_frame_callback_) {
				if (!send_frame_callback_(channelId, rtp_pkt)) {
					return false;
				}
			}

			frame_buf += (MAX_RTP_PAYLOAD_SIZE - 3);
			frame_size -= (MAX_RTP_PAYLOAD_SIZE - 3);

			FU[2] &= ~0x80;
		}

		{
			rtp_pkt.size = RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE + 3 +
				       static_cast<uint16_t>(frame_size);
			rtp_pkt.last = 1;
			uint8_t *rtp_pkt_data = rtp_pkt.data.get() +
						RTP_TCP_HEAD_SIZE +
						RTP_HEADER_SIZE;

			FU[2] |= 0x40;
			*(rtp_pkt_data++) = FU[0];
			*(rtp_pkt_data++) = FU[1];
			*(rtp_pkt_data++) = FU[2];
			memcpy(rtp_pkt_data, frame_buf, frame_size);

			if (send_frame_callback_) {
				if (!send_frame_callback_(channelId, rtp_pkt)) {
					return false;
				}
			}
		}
	}

	return true;
}

uint32_t H265Source::GetTimestamp()
{
	/* #if defined(__linux) || defined(__linux__) 
	struct timeval tv = {0};
	gettimeofday(&tv, NULL);
	uint32_t ts = ((tv.tv_sec*1000)+((tv.tv_usec+500)/1000))*90; // 90: _clockRate/1000;
	return ts;
#else */
	//auto time_point = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
	//auto time_point = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
	const auto time_point = chrono::time_point_cast<chrono::microseconds>(
		chrono::steady_clock::now());
	return static_cast<uint32_t>(
		(time_point.time_since_epoch().count() + 500) / 1000 * 90);
	//#endif
}
